/******************************************************
*  Copyright(c) pages.ilovezxl.top
*  FileName :thread_linux.c
*  Author : rivulet.dave
*  Description : 系统接口LINUX实现
*  Data : 2020-07-10
******************************************************/

/*********************************************************************
 * INCLUDES
 */


#include "stdrope.h"

#include "sallib.h"
#include "salagent.h"
#include "link.h"

#if (SAL_PRODUCER == _SAL_LINUX_)
#include <pthread.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <sys/utsname.h>
#include <sched.h>
#include <signal.h>
#include <errno.h>

#include "mem.h"
#include "thread.h"
#include "thread_linux.h"

/*********************************************************************
 *  EXTERNAL VARIABLES
 */

/*********************************************************************
 * MACROS
 */

#define OS_LINUX_API    0x00000009

/*********************************************************************
 * TYPES
 */

typedef void *(*linux_thread_func_t)(void*);
typedef struct _linux_thread_cb
{
    SLINK_NODE_S stLinkNode;
    osThreadState_t iThreadState;
    const char *pcName;
    void *pStackMemory;
    uint32_t uiStackSize;
    int32_t iPriority;
    pthread_t iThreadId;
    pthread_attr_t stThreadAttr;
    void *pArg;
    linux_thread_func_t pfThreadEntry;
} LINUX_THREAD_CB_S;

/*********************************************************************
 * FUNCTIONS DECLARE
 */

//  ==== Kernel Management Functions ====
static osStatus_t osKernelInitializeByLinux(void);
static osStatus_t osKernelGetInfoByLinux(osVersion_t *version, char *id_buf, uint32_t id_size);
static osKernelState_t osKernelGetStateByLinux(void);
static osStatus_t osKernelStartByLinux(void);
static uint32_t osKernelSuspendByLinux(void);
static void osKernelResumeByLinux(uint32_t sleep_ticks);
// ==== Thread Management Functions ====
static osThreadId_t osThreadNewByLinux(osThreadFunc_t func, void *argument, const osThreadAttr_t *attr);
static const char *osThreadGetNameByLinux(osThreadId_t thread_id);
static osThreadId_t osThreadGetIdByLinux(void);
static osThreadState_t osThreadGetStateByLinux(osThreadId_t thread_id);
static uint32_t osThreadGetStackSizeByLinux(osThreadId_t thread_id);
static uint32_t osThreadGetStackSpaceByLinux(osThreadId_t thread_id);
static osStatus_t osThreadSetPriorityByLinux(osThreadId_t thread_id, osPriority_t priority);
static osPriority_t osThreadGetPriorityByLinux(osThreadId_t thread_id);
static osStatus_t osThreadYieldByLinux(void);
static osStatus_t osThreadSuspendByLinux(osThreadId_t thread_id);
static osStatus_t osThreadResumeByLinux(osThreadId_t thread_id);
static osStatus_t osThreadDetachByLinux(osThreadId_t thread_id);
static osStatus_t osThreadJoinByLinux(osThreadId_t thread_id);
static void osThreadExitByLinux(void);
static osStatus_t osThreadTerminateByLinux(osThreadId_t thread_id);
static uint32_t osThreadGetCountByLinux(void);
static uint32_t osThreadEnumerateByLinux(osThreadId_t *thread_array, uint32_t array_items);

/*********************************************************************
 *  VARIABLES
 */

static OS_THREAD_BASE_S s_stOSThreadLinuxObj;
static SLINK_S s_stLinuxThreadList;

/*********************************************************************
 * FUNCTIONS
 */

/**
 *   @Description: 获取系统时间对象
 *   @param : NA
 *   @return: 对象地址
 */
void *osThreadLinuxObjNew(void)
{
    memset(&s_stOSThreadLinuxObj, 0, sizeof(OS_THREAD_BASE_S));
    
    s_stOSThreadLinuxObj.pfOSKernelInitialize = osKernelInitializeByLinux;
    s_stOSThreadLinuxObj.pfOSKernelGetInfo = osKernelGetInfoByLinux;
    s_stOSThreadLinuxObj.pfOSKernelGetState = osKernelGetStateByLinux;
    s_stOSThreadLinuxObj.pfOSKernelStart = osKernelStartByLinux;
    s_stOSThreadLinuxObj.pfOSKernelSuspend = osKernelSuspendByLinux;
    s_stOSThreadLinuxObj.pfOSKernelResume = osKernelResumeByLinux;
    
    s_stOSThreadLinuxObj.pfOSThreadNew = osThreadNewByLinux;
    s_stOSThreadLinuxObj.pfOSThreadGetName = osThreadGetNameByLinux;
    s_stOSThreadLinuxObj.pfOSThreadGetId = osThreadGetIdByLinux;
    s_stOSThreadLinuxObj.pfOSThreadGetState = osThreadGetStateByLinux;
    s_stOSThreadLinuxObj.pfOSThreadGetStackSize = osThreadGetStackSizeByLinux;
    s_stOSThreadLinuxObj.pfOSThreadGetStackSpace = osThreadGetStackSpaceByLinux;
    s_stOSThreadLinuxObj.pfOSThreadSetPriority = osThreadSetPriorityByLinux;
    s_stOSThreadLinuxObj.pfOSThreadGetPriority = osThreadGetPriorityByLinux;
    s_stOSThreadLinuxObj.pfOSThreadYield = osThreadYieldByLinux;
    s_stOSThreadLinuxObj.pfOSThreadSuspend = osThreadSuspendByLinux;
    s_stOSThreadLinuxObj.pfOSThreadResume = osThreadResumeByLinux;
    s_stOSThreadLinuxObj.pfOSThreadDetach = osThreadDetachByLinux;
    s_stOSThreadLinuxObj.pfOSThreadJoin = osThreadJoinByLinux;
    s_stOSThreadLinuxObj.pfOSThreadExit = osThreadExitByLinux;
    s_stOSThreadLinuxObj.pfOSThreadTerminate = osThreadTerminateByLinux;
    s_stOSThreadLinuxObj.pfOSThreadGetCount = osThreadGetCountByLinux;
    s_stOSThreadLinuxObj.pfOSThreadEnumerate = osThreadEnumerateByLinux;

    return &s_stOSThreadLinuxObj;
}

//  ==== Kernel Management Functions ====
static void osKernelSignalHandle(int arg)
{
	signal(SIGPIPE, osKernelSignalHandle);
	return;
}

static osStatus_t osKernelInitializeByLinux(void)
{
    SLINK_Init(&s_stLinuxThreadList);
    int32_t ret = 0;
	struct rlimit rlp;

    // 设置core文件大小限制
	getrlimit(RLIMIT_CORE, &rlp);
	rlp.rlim_cur = 500 * 1024 * 1024;// RLIM_INFINITY;	// unit:bytes //size:unlimits modified by tiger.56
	ret = setrlimit(RLIMIT_CORE, &rlp);
	if (ret < 0)
	{
		printf("set Maximum size of core file failed:[%d-%s]\n", errno, strerror(errno));
	}
	ret = system("sudo echo /opt/core.%e >  /proc/sys/kernel/core_pattern");
	if (ret < 0)
	{
		printf("set core file path failed:[%d-%s]\n", errno, strerror(errno));
	}

    // 补获PIPE信号
	signal(SIGPIPE, osKernelSignalHandle);
    return osOK;
}

static osStatus_t osKernelGetInfoByLinux(osVersion_t *version, char *id_buf, uint32_t id_size)
{
    struct utsname kernel_info;
    int ret = uname(&kernel_info);
    
    version->api = OS_LINUX_API;
    if (ret == 0) 
    {
        int m, n, r = 0;
        sscanf(kernel_info.release, "%d.%d.%d", &m, &n, &r);
        version->kernel = ((m << 24) & 0xFF000000) | ((n << 16) & 0x00FF0000) | (r & 0x0000FFFF);
        return osOK;
    }
    else
    {
        return osError;
    }
}

static osKernelState_t osKernelGetStateByLinux(void)
{
    return osKernelRunning;
}

static osStatus_t osKernelStartByLinux(void)
{
    return osOK;
}

static uint32_t osKernelSuspendByLinux(void)
{
    return 0;
}

static void osKernelResumeByLinux(uint32_t sleep_ticks)
{
}

// ==== Thread Management Functions ====
static osThreadId_t osThreadNewByLinux(osThreadFunc_t func, void *argument, const osThreadAttr_t *attr)
{
    int32_t iRet = 0;
    LINUX_THREAD_CB_S *pstThreadCb = NULL;

    if (!attr || NULL == func)
    {
        return (osThreadId_t)NULL;
    }

    pstThreadCb = (LINUX_THREAD_CB_S *)osMemAlloc(sizeof(LINUX_THREAD_CB_S));
    if (!pstThreadCb)
    {
        return (osThreadId_t)NULL;
    }

    pthread_attr_init(&pstThreadCb->stThreadAttr);
    pthread_attr_setscope(&pstThreadCb->stThreadAttr, PTHREAD_SCOPE_SYSTEM);

    if (0 < attr->priority)
    {
        struct sched_param stThreadParam;

        iRet = mlockall(MCL_CURRENT | MCL_FUTURE);

        // 设置调度策略 SCHED_FIFO, SCHED_RR, and SCHED_OTHER
        iRet |= pthread_attr_setschedpolicy(&pstThreadCb->stThreadAttr, SCHED_FIFO);
        // 设置线程的调度参数
        stThreadParam.sched_priority = attr->priority;
        iRet |= pthread_attr_setschedparam(&pstThreadCb->stThreadAttr, &stThreadParam);
        // 应用调度优先级参数
        iRet |= pthread_attr_setinheritsched(&pstThreadCb->stThreadAttr, PTHREAD_EXPLICIT_SCHED);
        if (iRet)
        {
            goto EXIT;
        }
    }

    if (NULL != attr->stack_mem && 0 != attr->stack_size)
    {
        // 设置栈
        pthread_attr_setstack(&pstThreadCb->stThreadAttr, attr->stack_mem, attr->stack_size);
    }

    pstThreadCb->pArg = argument;
    pstThreadCb->pfThreadEntry = (linux_thread_func_t)func;
    iRet = pthread_create(&pstThreadCb->iThreadId, &pstThreadCb->stThreadAttr, pstThreadCb->pfThreadEntry, pstThreadCb->pArg);

EXIT:
    if (!iRet)
    {
        //创建成功
        pstThreadCb->iThreadState = osThreadReady;
        pstThreadCb->pcName = attr->name;
        pstThreadCb->iPriority = attr->priority;
        pstThreadCb->pStackMemory = attr->stack_mem;
        pstThreadCb->uiStackSize = attr->stack_size;
        SLINK_Append(&s_stLinuxThreadList, (SLINK_NODE_S *)pstThreadCb);
        return (osThreadId_t)pstThreadCb;
    }
    osMemFree((void **)&pstThreadCb);
    return (osThreadId_t)NULL;
}

static const char *osThreadGetNameByLinux(osThreadId_t thread_id)
{
    LINUX_THREAD_CB_S *pstThreadCb = (LINUX_THREAD_CB_S *)thread_id;

    if (!pstThreadCb)
    {
        return NULL;
    }
    return pstThreadCb->pcName;
}

static osThreadId_t osThreadGetIdByLinux(void)
{
    SLINK_NODE_S *pstTempNode;

    pstTempNode = s_stLinuxThreadList.pstHead;
    while (pstTempNode)
    {
        LINUX_THREAD_CB_S *pstThreadCb = (LINUX_THREAD_CB_S *)pstTempNode;
        if (pstThreadCb->iThreadId == pthread_self())
        {
            return (osThreadId_t)pstThreadCb;
        }

        pstTempNode = pstTempNode->pNext;
    }
    
    return (osThreadId_t)NULL;
}

static osThreadState_t osThreadGetStateByLinux(osThreadId_t thread_id)
{
    LINUX_THREAD_CB_S *pstThreadCb = (LINUX_THREAD_CB_S *)thread_id;

    if (!pstThreadCb)
    {
        return osThreadError;
    }
    return pstThreadCb->iThreadState;
}

static uint32_t osThreadGetStackSizeByLinux(osThreadId_t thread_id)
{
    size_t uiStackTotalSize = 0;
    LINUX_THREAD_CB_S *pstThreadCb = (LINUX_THREAD_CB_S *)thread_id;

    if (!pstThreadCb)
    {
        return 0;
    }

    pthread_attr_getstacksize(&pstThreadCb->stThreadAttr, &uiStackTotalSize);
    return (uint32_t)uiStackTotalSize;
}

static uint32_t osThreadGetStackSpaceByLinux(osThreadId_t thread_id)
{
    return 0;
}

static osStatus_t osThreadSetPriorityByLinux(osThreadId_t thread_id, osPriority_t priority)
{
    LINUX_THREAD_CB_S *pstThreadCb = (LINUX_THREAD_CB_S *)thread_id;
    struct sched_param stThreadParam;

    if (!pstThreadCb)
    {
        return osError;
    }

    pstThreadCb->iPriority = priority;
    stThreadParam.sched_priority = priority;
    if (0 != pthread_attr_setschedparam(&pstThreadCb->stThreadAttr, &stThreadParam))
    {
        return osError;
    }
    return osOK;
}

static osPriority_t osThreadGetPriorityByLinux(osThreadId_t thread_id)
{
    LINUX_THREAD_CB_S *pstThreadCb = (LINUX_THREAD_CB_S *)thread_id;

    if (!pstThreadCb)
    {
        return osPriorityNone;
    }
    return pstThreadCb->iPriority;
}

static osStatus_t osThreadYieldByLinux(void)
{
    if (0 != sched_yield())
    {
        return osError;
    }
    return osOK;
}

static osStatus_t osThreadSuspendByLinux(osThreadId_t thread_id)
{
    return osOK;
}

static osStatus_t osThreadResumeByLinux(osThreadId_t thread_id)
{
    return osOK;
}

static osStatus_t osThreadDetachByLinux(osThreadId_t thread_id)
{
    LINUX_THREAD_CB_S *pstThreadCb = (LINUX_THREAD_CB_S *)thread_id;

    if (!pstThreadCb)
    {
        return osError;
    }

    if (0 != pthread_detach(pstThreadCb->iThreadId))
    {
        return osError;
    }
    return osOK;
}

static osStatus_t osThreadJoinByLinux(osThreadId_t thread_id)
{
    LINUX_THREAD_CB_S *pstThreadCb = (LINUX_THREAD_CB_S *)thread_id;

    if (!pstThreadCb)
    {
        return osError;
    }

    if (0 != pthread_join(pstThreadCb->iThreadId, NULL))
    {
        return osError;
    }
    return osOK;
}

static void osThreadExitByLinux(void)
{
    pthread_exit(NULL);
}

static osStatus_t osThreadTerminateByLinux(osThreadId_t thread_id)
{
    LINUX_THREAD_CB_S *pstThreadCb = (LINUX_THREAD_CB_S *)thread_id;

    if (!pstThreadCb)
    {
        return osError;
    }

    if (0 != pthread_cancel(pstThreadCb->iThreadId))
    {
        return osError;
    }
    return osOK;
}

static uint32_t osThreadGetCountByLinux(void)
{
    return s_stLinuxThreadList.uiCount;
}

static uint32_t osThreadEnumerateByLinux(osThreadId_t *thread_array, uint32_t array_items)
{
    return 0;
}

#endif  // SAL_PRODUCER == _SAL_LINUX_

/************************************** The End Of File **************************************/



